package com.insight.aop;

import com.insight.service.OperationLogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;

/**
 * T121: 操作日志AOP切面
 * 自动记录所有Controller API调用的审计日志
 *
 * @author Insight Team
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class OperationLogAspect {

    private final OperationLogService operationLogService;
    private final ObjectMapper objectMapper;

    /**
     * 切入点: 所有Controller方法
     */
    @Pointcut("execution(* com.insight.controller..*(..))")
    public void controllerMethods() {
    }

    /**
     * 环绕通知: 记录API调用
     */
    @Around("controllerMethods()")
    public Object logOperation(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        // 获取请求上下文
        ServletRequestAttributes attributes =
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        if (attributes == null) {
            // 非Web请求上下文,直接执行
            return joinPoint.proceed();
        }

        HttpServletRequest request = attributes.getRequest();

        // 获取方法信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 提取操作信息
        String operationType = extractOperationType(method);
        String module = extractModule(joinPoint.getTarget().getClass());
        String description = extractDescription(method);
        String requestMethod = request.getMethod();
        String requestUrl = request.getRequestURI();
        String ip = getClientIp(request);
        String userAgent = request.getHeader("User-Agent");

        try {
            // 执行目标方法
            Object result = joinPoint.proceed();

            // 记录成功日志
            long executionTime = System.currentTimeMillis() - startTime;
            logSuccessOperation(operationType, module, description, requestMethod,
                              requestUrl, ip, userAgent, executionTime);

            return result;

        } catch (Exception e) {
            // 记录失败日志
            long executionTime = System.currentTimeMillis() - startTime;
            logFailureOperation(operationType, module, description, requestMethod,
                              requestUrl, ip, userAgent, e, executionTime);

            throw e;
        }
    }

    /**
     * 提取操作类型
     */
    private String extractOperationType(Method method) {
        if (method.isAnnotationPresent(PostMapping.class)) {
            return "CREATE";
        } else if (method.isAnnotationPresent(PutMapping.class) ||
                   method.isAnnotationPresent(PatchMapping.class)) {
            return "UPDATE";
        } else if (method.isAnnotationPresent(DeleteMapping.class)) {
            return "DELETE";
        } else if (method.isAnnotationPresent(GetMapping.class)) {
            return "QUERY";
        } else {
            return "OPERATION";
        }
    }

    /**
     * 提取模块名称
     */
    private String extractModule(Class<?> controllerClass) {
        String className = controllerClass.getSimpleName();
        // DataSourceController -> DATASOURCE
        // MetadataController -> METADATA
        return className.replace("Controller", "").toUpperCase();
    }

    /**
     * 提取操作描述
     */
    private String extractDescription(Method method) {
        // 从方法名提取描述
        String methodName = method.getName();

        // 常见方法名模式转换
        if (methodName.startsWith("create")) return "创建" + extractResource(methodName);
        if (methodName.startsWith("update")) return "更新" + extractResource(methodName);
        if (methodName.startsWith("delete")) return "删除" + extractResource(methodName);
        if (methodName.startsWith("get")) return "查询" + extractResource(methodName);
        if (methodName.startsWith("list")) return "列表查询" + extractResource(methodName);
        if (methodName.startsWith("search")) return "搜索" + extractResource(methodName);
        if (methodName.startsWith("export")) return "导出" + extractResource(methodName);

        return methodName;
    }

    /**
     * 从方法名提取资源名称
     */
    private String extractResource(String methodName) {
        // createDataSource -> 数据源
        // getTableMetadata -> 表元数据
        if (methodName.contains("DataSource")) return "数据源";
        if (methodName.contains("Table")) return "表";
        if (methodName.contains("Metadata")) return "元数据";
        if (methodName.contains("Export")) return "导出";
        if (methodName.contains("Relationship")) return "关系";

        return "";
    }

    /**
     * 记录成功操作
     */
    private void logSuccessOperation(String operationType, String module, String description,
                                     String requestMethod, String requestUrl,
                                     String ip, String userAgent, Long executionTime) {
        try {
            operationLogService.logWithClient(
                operationType, module, description,
                requestUrl, ip, userAgent, executionTime
            );
        } catch (Exception e) {
            log.error("记录操作日志失败", e);
        }
    }

    /**
     * 记录失败操作
     */
    private void logFailureOperation(String operationType, String module, String description,
                                     String requestMethod, String requestUrl,
                                     String ip, String userAgent, Exception exception,
                                     Long executionTime) {
        try {
            operationLogService.logException(
                operationType, module, description,
                requestUrl, exception, executionTime
            );
        } catch (Exception e) {
            log.error("记录操作日志失败", e);
        }
    }

    /**
     * 获取客户端真实IP
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        // 处理多个IP的情况,取第一个
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }

        return ip;
    }
}
